/*******************************************************************************
 * Copyright (c) 2012, 2013 XML-Print Project.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     XML-Print Project - initial API and implementation
 ******************************************************************************/
package info.xmlprint.ui.referencesystem;

import java.util.ArrayList;
import java.util.List;

import info.xmlprint.core.project.PrintProject;
import info.xmlprint.core.referencesystem.AbstractBaseGroup;
import info.xmlprint.core.referencesystem.AbstractBaseItem;
import info.xmlprint.core.referencesystem.ReferenceField;
import info.xmlprint.core.referencesystem.ReferenceFieldGroup;
import info.xmlprint.ui.message.GuiMessages;
import info.xmlprint.ui.widgets.IObserverControl;
import info.xmlprint.ui.widgets.ISetupComposite;
import info.xmlprint.ui.widgets.ISubjectControl;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;

/*
 * a ReferenceField fill a Composite
 */
public class ReferenceFieldComposite extends Composite implements
		ISubjectControl, ISetupComposite {
	private ReferenceField referenceField;
	private Text nameText, xPathText, xmlAttributeText, startValueText,
			resetXpathText, titleText;
	private Button formatRadio, contentRadio, xPathRadio, xmlAttributeRadio,
			continuousRadio, noResetRadio, fieldRadio, resetXpathRadio;
	private Combo formatCombo, contentCombo, resetCombo, titleCombo;

	private ReferenceFieldComposite _this = this;

	private Group nameGroup, contentGroup, counterGroup, resetGroup,
			titleGroup;

	private List<IObserverControl> observerList = new ArrayList<IObserverControl>();

	private ReferenceFieldGroup referenceFieldGroup;

	private String[][] contentComboNames = {
			{ "line", GuiMessages.Fieldinformation_LineNumber },
			{ "column", GuiMessages.Fieldinformation_ColumnNumber },
			{ "page", GuiMessages.Fieldinformation_PageNumber } };

	private PrintProject printProject;

	private String[] titleComboNames = {
			GuiMessages.Fieldinformation_Title_None,
			GuiMessages.Fieldinformation_General_Attribute,
			GuiMessages.Fieldinformation_Title_Content_Element,
			GuiMessages.Fieldinformation_Title_Content_XPath };

	public ReferenceFieldComposite(Composite parent, int style) {
		super(parent, style);
		this.setLayout(new GridLayout());
		createControls();
		// referenceFieldGroup = this. getReferenceFieldGroup();
	}

	private void createControls() {
		// name
		{
			nameGroup = createGroup("");
			nameGroup.setLayout(new GridLayout(2, false));
			new Label(nameGroup, SWT.NONE)
					.setText(GuiMessages.Fieldinformation_Name + ":");
			nameText = new Text(nameGroup, SWT.BORDER);
			nameText.setToolTipText("Geben Sie einen Namen für dieses Feld an"); ////$NON-NLS-N$
			setGridDataW(nameText, 100);
		}

		// structure
		{
			contentGroup = createGroup(GuiMessages.Fieldinformation_Content);
			contentGroup.setLayout(new GridLayout(2, false));

			/*
			 * Radiobutton for structures based on existing formats
			 */
			formatRadio = new Button(contentGroup, SWT.RADIO);
			formatRadio.setText(GuiMessages.Fieldinformation_Content_Format
					+ ":");
			setGridData(formatRadio);
			formatRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setContentControls();
				}
			});

			formatCombo = new Combo(contentGroup, SWT.READ_ONLY);

			/*
			 * Structures based on typographic elements
			 */
			contentRadio = new Button(contentGroup, SWT.RADIO);
			contentRadio
					.setText(GuiMessages.Fieldinformation_Content_Typographic
							+ ":");
			setGridData(contentRadio);
			contentRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setContentControls();
				}
			});

			contentCombo = new Combo(contentGroup, SWT.READ_ONLY);
			for (int i = 0; i < contentComboNames.length; i++) {
				contentCombo.add(contentComboNames[i][1]);
			}

			/*
			 * Structures based on XPath expressions
			 */

			xPathRadio = new Button(contentGroup, SWT.RADIO);
			xPathRadio
					.setText(GuiMessages.Fieldinformation_General_XPath + ":");
			setGridData(xPathRadio);
			xPathRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setContentControls();
				}
			});

			xPathText = new Text(contentGroup, SWT.BORDER);
			xPathText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
			xPathText
					.setToolTipText("Geben Sie einen gültigen XPath-Ausdruck ein");//$NON-NLS-N$
		}
		// counter
		{
			counterGroup = createGroup(GuiMessages.Fieldinformation_Counter);
			counterGroup.setLayout(new GridLayout(4, false));

			/*
			 * Counter value based on attribute
			 */
			xmlAttributeRadio = new Button(counterGroup, SWT.RADIO);
			xmlAttributeRadio
					.setText(GuiMessages.Fieldinformation_General_Attribute
							+ ":");
			setGridData(xmlAttributeRadio);
			xmlAttributeRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setCounterControls();
				}
			});

			xmlAttributeText = new Text(counterGroup, SWT.BORDER);
			xmlAttributeText
					.setToolTipText("Bitte geben Sie das gewünschte Attribut an, z.B. \u201En\u201C");//$NON-NLS-N$

			Label label0 = new Label(counterGroup, SWT.NONE);
			label0.setText("");
			setGridDataHZ(label0, 2);

			continuousRadio = new Button(counterGroup, SWT.RADIO);
			continuousRadio
					.setText(GuiMessages.Fieldinformation_Counter_Continuous);
			setGridDataHZ(continuousRadio, 2);
			continuousRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setCounterControls();
				}
			});

			Label initialValue = new Label(counterGroup, SWT.NONE);
			initialValue
					.setText(GuiMessages.Fieldinformation_Counter_InitialValue
							+ ":");
			setGridData(initialValue);

			startValueText = new Text(counterGroup, SWT.BORDER);
			setGridData(startValueText);

			// reset
			resetGroup = createGroup(GuiMessages.Fieldinformation_Reset);
			resetGroup.setLayout(new GridLayout(2, true));

			noResetRadio = new Button(resetGroup, SWT.RADIO);
			noResetRadio.setText(GuiMessages.Fieldinformation_noReset);
			setGridDataHZ(noResetRadio, 2);
			noResetRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setResetControls();
				}
			});

			fieldRadio = new Button(resetGroup, SWT.RADIO);
			fieldRadio.setText(GuiMessages.Fieldinformation_Field);
			setGridData(fieldRadio);
			fieldRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setResetControls();
				}
			});

			resetCombo = new Combo(resetGroup, SWT.NONE);
			setGridData(resetCombo);

			resetXpathRadio = new Button(resetGroup, SWT.RADIO);
			resetXpathRadio.setText(GuiMessages.Fieldinformation_General_XPath
					+ ":");
			setGridData(resetXpathRadio);
			resetXpathRadio.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setResetControls();
				}
			});
			resetXpathText = new Text(resetGroup, SWT.BORDER);
			resetXpathText
					.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		}

		// title
		{
			titleGroup = createGroup(GuiMessages.Fieldinformation_Title);
			titleGroup.setLayout(new GridLayout(2, false));
			titleCombo = new Combo(titleGroup, SWT.NONE);
			titleCombo.setItems(titleComboNames);
			setGridDataHZ(titleCombo, 2);
			titleCombo.addListener(SWT.Selection, new Listener() {
				@Override
				public void handleEvent(Event event) {
					setTitleControls();
				}

			});

			new Label(titleGroup, SWT.NONE)
					.setText(GuiMessages.Fieldinformation_Title_Input + ":");
			titleText = new Text(titleGroup, SWT.BORDER);
			setGridDataW(titleText, 200);
		}

		new Label(this, SWT.NONE);
		Composite footComp = new Composite(this, SWT.BOTTOM);
		setGridData(footComp);
		footComp.setLayout(new FillLayout());
		Button saveChangesButton = new Button(footComp, SWT.NONE);
		saveChangesButton.setText(GuiMessages.Stileditor_Gui_SaveChanges);
		saveChangesButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				String fieldName = nameText.getText().trim();

				// empty name
				if (fieldName.isEmpty() || fieldName.equalsIgnoreCase("")) {
					MessageDialog.openError(null, "",
							GuiMessages.Stileditor_Error_NoNameForFieldGiven);
					return;
				}

				// is name already used
				boolean used = false;
				if (referenceField == null) {
					if (referenceFieldGroup.hasItem(fieldName)) {
						used = true;
					}
				} else {
					AbstractBaseItem field = referenceFieldGroup
							.getItem(fieldName);
					if (field != null && !referenceField.equals(field))
						used = true;
				}

				if (used) {
					MessageDialog
							.openError(
									null,
									"",
									GuiMessages.Stileditor_Error_NameForFieldAlreadyUsed);
					return;
				}

				// check, whether a reset level is selected for typographic
				// counters
				if (contentRadio.getSelection() && noResetRadio.getSelection()) {
					MessageDialog
							.openError(
									null,
									"", "Für typographische Zähler müssen Sie einen Reset angeben. Globale Zähler sind bereits vordefiniert."); //$NON-NLS-N$
					return;
				}

				if (referenceField == null) {
					referenceField = new ReferenceField();
					updateReferenceField();
					referenceFieldGroup.add(referenceField);
				} else {
					updateReferenceField();
				}
				notifyObservers();
				_this.getShell().close();
			}

		});

		Button verwerfenButton = new Button(footComp, SWT.NONE);
		verwerfenButton.setText(GuiMessages.Stileditor_Gui_DiscardChanges);
		verwerfenButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				_this.getShell().close();
			}
		});

	}

	private Group createGroup(String title) {
		Group group = new Group(this, SWT.NONE);
		group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		group.setText(title);
		return group;
	}

	private void fillControls() {
		// resetControls();

		if (referenceField == null) {
			resetCombo.removeAll();
			if (referenceFieldGroup.getItemCount() > 0) {
				for (String s : referenceFieldGroup.getItemNames()) {
					resetCombo.add(s);
				}
			}
			resetCombo.select(0);
		}
		if (referenceField != null) {
			// name
			nameText.setText(referenceField.getValueOf("name"));

			// content
			String select = referenceField.getValueOf("content-select");
			String content;
			/*
			 * 0: Formats 1: Typographic content 2: XPath element
			 */
			switch (select.trim()) {
			case "0":
				formatRadio.setSelection(true);

				break;
			case "1":
				contentRadio.setSelection(true);
				break;
			case "2":
				xPathRadio.setSelection(true);
			}

			/*
			 * Former values for combo boxes are selected in any case
			 */
			content = referenceField.getValueOf("content-value-format");
			for (int i = 0; i < formatCombo.getItemCount(); i++) {
				if (content.equals(formatCombo.getItem(i))) {
					formatCombo.select(i);
					break;
				}
			}
			content = referenceField.getValueOf("content-value-typo");
			for (int i = 0; i < contentComboNames.length; i++) {
				if (content.equalsIgnoreCase(contentComboNames[i][0])) {
					contentCombo.select(i);
					break;
				}
			}

			xPathText.setText(referenceField.getValueOf("content-xPath"));

			// counter
			select = referenceField.getValueOf("counter-select");
			xmlAttributeText.setText(referenceField
					.getValueOf("counter-attribute"));
			startValueText.setText(referenceField
					.getValueOf("counter-startValue"));

			boolean b = true;
			if (!select.equalsIgnoreCase("0")) {
				b = false;
			}

			xmlAttributeRadio.setSelection(b);
			continuousRadio.setSelection(!b);

			/*
			 * Reset values 0: no reset 1: reset at another field 2: reset at
			 * another structure XPath
			 */
			b = true;
			int i = 0;
			String fieldName = referenceField.getValueOf("name");
			resetCombo.removeAll();
			for (String s : referenceFieldGroup.getItemNames()) {
				if (s.equals(fieldName))
					continue;

				resetCombo.add(s);
			}

			resetXpathText.setText(referenceField.getValueOf("reset-xPath"));
			select = referenceField.getValueOf("reset-select");
			switch (select) {
			case "0":
				noResetRadio.setSelection(true);
				fieldRadio.setSelection(false);
				resetXpathRadio.setSelection(false);
				break;
			case "1":
				noResetRadio.setSelection(false);
				fieldRadio.setSelection(true);
				resetXpathRadio.setSelection(false);
				break;
			case "2":
				noResetRadio.setSelection(false);
				fieldRadio.setSelection(false);
				resetXpathRadio.setSelection(true);
				break;
			}

			if (resetCombo.getItemCount() == 0) {
				if (fieldRadio.getSelection())
					noResetRadio.setSelection(true);
				fieldRadio.setSelection(false);
				setResetControls();
				fieldRadio.setEnabled(false);
			} else {
				fieldRadio.setEnabled(true);
				String resetName = referenceField.getValueOf("reset-name");
				if (!resetName.equalsIgnoreCase("")) {
					for (int j = 0; j < resetCombo.getItemCount(); j++) {
						if (resetCombo.getItem(j).equals(resetName)) {
							i = j;
							break;
						}
					}
				}
				resetCombo.select(i);
			}

			/*
			 * Title values 0: no title 1: Title based on XML attribute (given
			 * in text field) 2: Content of recent element 3: Content of another
			 * element (given by XPath expression in text field)
			 */
			i = 0;
			String intStr = referenceField.getValueOf("title-select").trim();
			if (!intStr.equalsIgnoreCase("")) {
				i = Integer.parseInt(intStr);
			}
			titleCombo.select(i);
			titleText.setText(referenceField.getValueOf("title-xPath"));
			titleText
					.setToolTipText("Bitte geben Sie das gewünschte Attribut, "
							+ "das den Titel enthält bzw. den XPath-Ausdruck des entsprechenden Elements an");
		}

		setContentControls();
		setResetControls();
		setCounterControls();
		setTitleControls();
		nameText.setFocus();
	}

	public ReferenceField getReferenceField() {
		return referenceField;
	}

	@Override
	public void notifyObservers() {
		for (IObserverControl o : observerList) {
			o.updateControl();
		}
	}

	@Override
	public void registerObserver(IObserverControl observer) {
		observerList.add(observer);
	}

	@Override
	public void removeObserver(IObserverControl observer) {
		observerList.remove(observer);
	}

	private void resetControlsValue() {
		nameText.setText("");

		formatRadio.setSelection(true);
		formatCombo.select(0);
		contentRadio.setSelection(false);
		contentCombo.select(0);
		xPathRadio.setSelection(false);
		xPathText.setText("");
		setContentControls();

		xmlAttributeRadio.setSelection(true);
		xmlAttributeText.setText("");
		continuousRadio.setSelection(false);
		startValueText.setText("");
		setCounterControls();

		noResetRadio.setSelection(true);
		fieldRadio.setSelection(false);
		resetCombo.select(0);
		resetXpathRadio.setSelection(false);
		resetXpathText.setText("");
		setResetControls();

		titleCombo.select(0);
		titleText.setText("");
		setTitleControls();
	}

	private void setContentControls() {
		// check, whether formatradio has been selected although there is not
		// format available -> check contentRadio and trigger event
		if (formatCombo.getItemCount() == 0) {
			if (formatRadio.getSelection())
				contentRadio.setSelection(true);
			// setContentControls();
			formatRadio.setSelection(false);
		}

		if (formatRadio.getSelection()) {
			contentRadio.setSelection(false);
			xPathRadio.setSelection(false);
			formatCombo.setEnabled(true);
			contentCombo.setEnabled(false);
			xPathText.setEnabled(false);
			// activate counter and title group
			counterGroup.setEnabled(true);
			titleGroup.setEnabled(true);
		} else if (contentRadio.getSelection()) {
			formatRadio.setSelection(false);
			xPathRadio.setSelection(false);
			contentCombo.setEnabled(true);
			formatCombo.setEnabled(false);
			xPathText.setEnabled(false);
			// deactivate counter and title group
			counterGroup.setEnabled(false);
			titleGroup.setEnabled(false);
		} else {
			formatRadio.setSelection(false);
			contentRadio.setSelection(false);
			xPathText.setEnabled(true);
			contentCombo.setEnabled(false);
			formatCombo.setEnabled(false);
			// activate counter and title group
			counterGroup.setEnabled(true);
			titleGroup.setEnabled(true);
			// setCounterControls();
		}
	}

	private void setCounterControls() {
		boolean b = true;
		if (xmlAttributeRadio.getSelection()) {
			b = false;
			continuousRadio.setSelection(false);
		} else {
			continuousRadio.setSelection(true);
		}
		xmlAttributeText.setEnabled(!b);
		startValueText.setEnabled(b);
	}

	private GridData setGridData(Control control) {
		GridData gd = new GridData();
		gd.verticalAlignment = 0;
		control.setLayoutData(gd);
		return gd;
	}

	private void setGridDataHZ(Control control, int i) {
		GridData gd = setGridData(control);
		gd.horizontalSpan = i;
		control.setLayoutData(gd);
	}

	private void setGridDataW(Control control, int i) {
		GridData gd = setGridData(control);
		gd.widthHint = i;
		control.setLayoutData(gd);
	}

	public void setInput(ReferenceField field) {
		referenceField = field;
		updateFormatCombo();

		if (field == null) {// TODO: Check why this is not working at all. Do we
							// need resetControls before fillControls?
			resetControlsValue();
		} else
			fillControls();
	}

	@Override
	public void setPrintProject(PrintProject pProject) {
		printProject = pProject;
	}

	public void setReferenceGroup(AbstractBaseGroup referenceFieldGroup) {
		this.referenceFieldGroup = (ReferenceFieldGroup) referenceFieldGroup;
	}

	private void setResetControls() {
		if (noResetRadio.getSelection()) {
			resetCombo.setEnabled(false);
			resetXpathRadio.setSelection(false);
			resetXpathText.setEnabled(false);
		} else if (fieldRadio.getSelection()) {
			resetCombo.setEnabled(true);
			resetXpathRadio.setSelection(false);
			resetXpathText.setEnabled(false);
		} else {
			resetCombo.setEnabled(false);
			resetXpathRadio.setSelection(true);
			resetXpathText.setEnabled(true);
		}
	}

	private void setTitleControls() {
		int index = titleCombo.getSelectionIndex();
		if (index == 1 || index == 3) {
			titleText.setEnabled(true);
		} else {
			titleText.setEnabled(false);
		}
	}

	private void updateFormatCombo() {
		/*
		 * Add only non-default formats
		 */
		formatCombo.removeAll();
		String[] formatNames = printProject.getFormatGroup().getFormatNames();
		for (int i = 0; i < formatNames.length; i++) {
			if (formatNames[i].equalsIgnoreCase("standard")
					|| formatNames[i].equalsIgnoreCase("ignore"))
				continue;
			formatCombo.add(formatNames[i]);
		}
	}

	private void updateReferenceField() {
		referenceField.setValueOf("name", nameText.getText());

		// content
		if (formatRadio.getSelection()) {
			referenceField.setValueOf("content-select", "0");
		} else if (contentRadio.getSelection()) {
			referenceField.setValueOf("content-select", "1");
		} else { // xpathRadio
			referenceField.setValueOf("content-select", "2");
		}

		// Save all values, not just that of the selected option
		referenceField.setValueOf("content-value-format", formatCombo.getText());
		referenceField.setValueOf("content-value-typo",
				contentComboNames[contentCombo.getSelectionIndex()][0]);
		referenceField.setValueOf("content-xPath", xPathText.getText());

		// set the combo boxes
		/*
		 * int formatComboIndex = formatCombo.getSelectionIndex(); if
		 * (formatComboIndex > -1)
		 * referenceField.setValue("content-value-format",
		 * formatCombo.getItem(formatComboIndex));
		 * 
		 * int contentComboIndex = contentCombo.getSelectionIndex();
		 * referenceField.setValue("content-value-typo",
		 * contentCombo.getItem(contentComboIndex));
		 */
		// nodeAddChildren(, {"content-select", "content-value-format",
		// "content-value-typo", "content-xPath"});

		// counter
		if (xmlAttributeRadio.getSelection()) {
			referenceField.setValueOf("counter-select", "0");
		} else {
			referenceField.setValueOf("counter-select", "1");
		}
		referenceField
				.setValueOf("counter-attribute", xmlAttributeText.getText());
		referenceField.setValueOf("counter-startValue", startValueText.getText());

		if (noResetRadio.getSelection()) {
			referenceField.setValueOf("reset-select", "0");
		} else if (fieldRadio.getSelection()) {
			referenceField.setValueOf("reset-select", "1");
		} else {
			referenceField.setValueOf("reset-select", "2");
		}

		// reset
		referenceField.setValueOf("reset-name", resetCombo.getText());
		referenceField.setValueOf("reset-xPath", resetXpathText.getText());

		// title
		int titleComboIndex = titleCombo.getSelectionIndex();
		referenceField.setValueOf("title-select", titleComboIndex + "");
		if (titleComboIndex == 1) {
			referenceField.setValueOf("title-attribute", titleText.getText());
		} else if (titleComboIndex == 3) {
			referenceField.setValueOf("title-xPath", titleText.getText());
		}

	}
}
